class TreeElementWithInfo(object):
  def __init__(self):
    self.doBranch_by_iId = {};
    self.dInfo_xValue_by_sDescription = {};
  
  @property
  def bHasContent(self):
    return len(self.dInfo_xValue_by_sDescription) > 0 and \
        len([oBranch for oBranch in self.doBranch_by_iId.values() if oBranch]) > 0;
  
  def addInfo(self, sDescription, xValue):
    self.dInfo_xValue_by_sDescription[sDescription] = xValue;
  
  def getInfo(self, sDescription, xDefaultValue = None):
    return self.dInfo_xValue_by_sDescription.get(sDescription, xDefaultValue);
  
  def getOrAddBranch(self, iId):
    assert self.cBranchClass, "Cannot creat branches in %s" % self.__class__.__name__;
    if iId not in self.doBranch_by_iId:
      self.doBranch_by_iId[iId] = self.cBranchClass();
    return self.doBranch_by_iId[iId];

  def getBranch(self, iId):
    return self.doBranch_by_iId[iId];
  
  def removeBranch(self, iId):
    self.doBranch_by_iId[iId] = None;
  
  def getBranchesById(self):
    doBranch_by_iId = {}
    for iId, oBranch in self.doBranch_by_iId.items():
      if oBranch:
        doBranch_by_iId[iId] = oBranch;
    return doBranch_by_iId;

  @property
  def sDescription(self):
    sFormat = self.getDescriptionFormat();
    if sFormat is None:
      return self.getInfo("type") or "Unknown";
    def formatData(oFormatMatch):
      sDescription = oFormatMatch.group(1);
      if oFormatMatch.group(2):
        sAltText = oFormatMatch.group(2)[1:]
      else:
        sAltText = oFormatMatch.group(0);
      return str(self.getInfo(sDescription, sAltText));
    import re;
    return re.sub(r"\{" r"([^\}\|]+)" r"(\|[^\}]*)?" r"\}", formatData, sFormat);
  
  def treeDump(self):
    asDump = [];
    for iId, oBranch in self.doBranch_by_iId.items():
      if oBranch:
        asDump.append("* %s #%d: %s" % (self.cBranchClass.__name__, iId, oBranch.sDescription));
        for sLine in oBranch.treeDump():
          asDump.append("  " + sLine);
    return asDump;
  
  def debugDump(self):
    asDescriptions = self.dInfo_xValue_by_sDescription.keys();
    asDescriptions.sort();
    asDump = ["+ %s: %s" % (sDescription, repr(self.dInfo_xValue_by_sDescription[sDescription]))
        for sDescription in asDescriptions];
    for iId, oBranch in self.doBranch_by_iId.items():
      if oBranch:
        asDump.append("* %s #%d: %s" % (self.cBranchClass.__name__, iId, oBranch.sDescription));
        for sLine in oBranch.debugDump():
          asDump.append("  " + sLine);
    return asDump;

class Stream(TreeElementWithInfo):
  def getDescriptionFormat(self):
    return {
      "Video": "Video: {video size|unknown size} ({video aspect ratio|unknown aspect ratio}) " \
                  "@{video framerate|unknown }fps, {codec full name|unknown codec}, {bitrate|unknown bitrate}",
      "Audio": "Audio: {language name|unknown laguage}, {codec full name|unknown code} {name|}, " \
                  "sample rate {audio sample rate|unknown}, {bitrate|unknown bitrate}",
      "Subtitles": "{codec full name|Subtitle}: {language name|unknown language}",
    }.get(self.getInfo("type"), "{type|Unknown type}");

class Title(TreeElementWithInfo):
  cBranchClass = Stream;
  def __init__(self):
    self.sTitle = None;
    TreeElementWithInfo.__init__(self);
  def getDescriptionFormat(self):
    return "{duration|unknown duration}, {chapter count|unknown number of} chapter(s), {size string|unknown size}";

class Disc(TreeElementWithInfo):
  cBranchClass = Title;
  def __init__(self, sDrivePath, sDeviceName, iYear, bIsMovie):
    self.sDrivePath = sDrivePath;
    self.sDeviceName = sDeviceName;
    self.sName = None;
    self.iYear = iYear;
    self.bIsMovie = bIsMovie;
    self.iSeason = None;
    self.iFirstEpisodeNumber = 1;
    TreeElementWithInfo.__init__(self);
  def getDescriptionFormat(self):
    return "{type|Disc}: {name} (%s)" % self.iYear;
